/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package batailledesardennes.util.geomFloat;

/**
 *
 * @author charly
 */
public class Rectangle extends Shape {
   private final Point botLeft;
   private float width;
   private float height;

   public Rectangle() {
      botLeft = new Point();
   }

   public Rectangle(Point p1, Point p2, boolean copyPoints) {
      if (copyPoints) {
         this.botLeft = p1.getCopy();
      } else {
         this.botLeft = p1;
      }
      float tmpX = p1.getX();
      if (p2.getX() < tmpX) {
         this.botLeft.setX(p2.getX());
         p2.setX(tmpX);
      }
      float tmpY = p1.getY();
      if (p2.getY() < tmpY) {
         this.botLeft.setY(p2.getY());
         p2.setY(tmpY);
      }
      width = p2.getX() - this.botLeft.getX();
      height = p2.getY() - this.botLeft.getY();
   }

   public Rectangle(Point botLeft, Point topRight) {
      this(botLeft, topRight, false);
   }

   public Rectangle(float x, float y, float width, float height) {
      botLeft = new Point(x, y);
      this.width = width;
      this.height = height;
   }

   public Rectangle(Point botLeft, float width, float height) {
      this.botLeft = botLeft.getCopy();
      this.width = width;
      this.height = height;
   }

   public Rectangle(Rectangle rect) {
      this(rect.getBotLeft(), rect.getWidth(), rect.getHeight());
   }

   public Rectangle setTwoPoints(float minX, float minY, float maxX, float maxY) {
      botLeft.setPos(minX, minY);
      width = maxX - minX;
      height = maxY - minY;
      return this;
   }

   public Point getBotLeft() {
      return botLeft;
   }

   public float getX() {
      return botLeft.getX();
   }

   public float getY() {
      return botLeft.getY();
   }

   public float getRightX() {
      return botLeft.getX() + width;
   }

   public float getTopY() {
      return botLeft.getY() + height;
   }

   public float getWidth() {
      return width;
   }

   public float getHeight() {
      return height;
   }

   public void setX(float x) {
      botLeft.setX(x);
   }

   public void setY(float y) {
      botLeft.setY(y);
   }

   public void setWidth(float width) {
      this.width = width;
   }

   public void setHeight(float height) {
      this.height = height;
   }

   @Override
   public boolean containsPoint(float x, float y) {
      return botLeft.getX() <= x && botLeft.getX() + width >= x
              && botLeft.getY() <= y && botLeft.getY() + height >= y;
   }

   @Override
   public boolean containsRect(float x, float y, float width, float height) {
      boolean contains = getX() <= x && getY() <= y && getRightX() >= x + width && getTopY() >= y + height;
      return contains;
   }

   /**
    * Check if rect1 contains rect2
    * @param r1x1 minx
    * @param r1y1
    * @param r1x2 max x
    * @param r1y2
    * @param r2x1
    * @param r2y1
    * @param r2x2
    * @param y2x2
    * @return
    */
   public static boolean rectContainsRect(float r1x1, float r1y1, float r1x2, float r1y2, float r2x1, float r2y1, float r2x2, float y2x2) {
      return r1x2 >= r2x2 && r1x1 <= r2x1 && r1y2 >= y2x2 && r1y1 <= r2y1;
   }

   @Override
   public float getArea() {
      return getWidth() * getHeight();
   }

   public static float getArea(float minX, float minY, float maxX, float maxY) {
      return (maxX - minX) * (maxY - minY);
   }

   @Override
   public float[] getBounds(float[] bounds) {
      if (bounds == null || bounds.length < 4) {
         bounds = new float[4];
      }
      bounds[0] = botLeft.getX();
      bounds[1] = botLeft.getY();
      bounds[2] = botLeft.getX() + width;
      bounds[3] = botLeft.getY() + height;
      return bounds;
   }

   @Override
   public Point getCenter() {
      return botLeft.getCopy().translate(width / 2, height / 2);
   }

   @Override
   public Point getClosestIntersectionWithLine(float x0, float y0, float x1, float y1) {
      //TODO all stuff with rect/lines
      return null;
   }

   @Override
   public Point getBoundPointFromCenterTowards(float x, float y) {
      //TODO
      return null;
   }

   @Override
   public Point getClosestPoint(float x, float y, float dist) {
      //TODO
      return null;
   }

   @Override
   public float getPerimeter() {
      return getWidth() * 2 + getHeight() * 2;
   }

   @Override
   public float getRadius() {
      return 0.5f * botLeft.distance(botLeft.getX() + width, botLeft.getY() + height);
   }

   @Override
   public boolean intersects(Shape other) {
      return other.intersectsRect(this);
   }

   @Override
   public boolean intersectsRect(float x, float y, float width, float height) {
      return botLeft.getX() <= x + width && botLeft.getY() <= y + height
              && botLeft.getX() + this.width >= x && botLeft.getY() + this.height >= y;
   }

   @Override
   public boolean intersectsWithLineSegment(float x0, float y0, float x1, float y1) {
      //TODO
      return false;
   }

   /**
    * This function will return a polygon
    * @param angle
    * @param x
    * @param y
    * @return
    */
   @Override
   public Polygon rotate(float angle, float x, float y) {
      Polygon p = toPolygon();
      return p.rotate(angle, x, y);
   }

   public Polygon toPolygon() {
      Polygon p = new Polygon(botLeft, botLeft.getCopy().translate(0, height),
                              botLeft.getCopy().translate(width, height), botLeft.getCopy().translate(width, 0));
      return p;
   }

   public boolean isValid() {
      return width >= 0 && height >= 0;
   }

   @Override
   public Rectangle scale(float sx, float sy, float x, float y) {
      botLeft.scale(sx, sy, x, y);
      width *= sx;
      height *= sy;
      return this;
   }

   @Override
   public Rectangle translate(float x, float y) {
      botLeft.translate(x, y);
      return this;
   }

   @Override
   public Rectangle translateTo(float x, float y) {
      botLeft.translateTo(x - getWidth() / 2, y - getHeight() / 2);
      return this;
   }

   @Override
   public Rectangle getCopy() {
      return new Rectangle(botLeft, width, height);
   }

   /**
    * Calculate the area by which a rectangle would be enlarged if
    * added to the passed rectangle..
    *
    * @param r1MinX minimum X coordinate of rectangle 1
    * @param r1MinY minimum Y coordinate of rectangle 1
    * @param r1MaxX maximum X coordinate of rectangle 1
    * @param r1MaxY maximum Y coordinate of rectangle 1
    * @param r2MinX minimum X coordinate of rectangle 2
    * @param r2MinY minimum Y coordinate of rectangle 2
    * @param r2MaxX maximum X coordinate of rectangle 2
    * @param r2MaxY maximum Y coordinate of rectangle 2
    *
    * @return enlargement
    */
   static public float enlargement(float r1MinX, float r1MinY, float r1MaxX, float r1MaxY,
                                   float r2MinX, float r2MinY, float r2MaxX, float r2MaxY) {
      float r1Area = (r1MaxX - r1MinX) * (r1MaxY - r1MinY);

      if (r1Area == Float.POSITIVE_INFINITY) {
         return 0; // cannot enlarge an infinite rectangle...
      }

      if (r2MinX < r1MinX) {
         r1MinX = r2MinX;
      }
      if (r2MinY < r1MinY) {
         r1MinY = r2MinY;
      }
      if (r2MaxX > r1MaxX) {
         r1MaxX = r2MaxX;
      }
      if (r2MaxY > r1MaxY) {
         r1MaxY = r2MaxY;
      }

      float r1r2UnionArea = (r1MaxX - r1MinX) * (r1MaxY - r1MinY);

      if (r1r2UnionArea == Float.POSITIVE_INFINITY) {
         // if a finite rectangle is enlarged and becomes infinite,
         // then the enlargement must be infinite.
         return Float.POSITIVE_INFINITY;
      }
      return r1r2UnionArea - r1Area;
   }
}
